package com.asa.voice.mp3.asa.decoder;


/**
 * 这个应该是解析mp3的头
 * @author asa
 *
 */
public final class Header {
	  public static final int MPEG1 = 3;
	  
	  public static final int MPEG2 = 2;
	  
	  public static final int MPEG25 = 0;
	  
	  private int[][][] bitrate = new int[][][] { { { 
	          0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 
	          320, 352, 384, 416, 448 }, { 
	          0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 
	          192, 224, 256, 320, 384 }, { 
	          0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 
	          160, 192, 224, 256, 320 } }, { { 
	          0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 
	          160, 176, 192, 224, 256 }, { 
	          0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 
	          96, 112, 128, 144, 160 }, { 
	          0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 
	          96, 112, 128, 144, 160 } } };
	  
	  private int[][] samplingRate = new int[][] { { 11025, 12000, 8000, 0 }, { 0, 0, 0, 0 }, { 22050, 24000, 16000, 0 }, { 44100, 48000, 32000, 0 } };
	  
	  private int verID;
	  
	  private int layer;
	  
	  private int protection_bit;
	  
	  private int bitrate_index;
	  
	  private int sampling_frequency;
	  
	  private int padding_bit;
	  
	  private int mode;
	  
	  private int mode_extension;
	  
	  private int framesize;
	  
	  private int maindatasize;
	  
	  private int sideinfosize;
	  
	  private int lsf;
	  
	  private int headerMask;
	  
	  private boolean isMS;
	  
	  private boolean isIntensity;
	  
	  private boolean sync;
	  
	  private int idx;
	  
	  private long trackLength;
	  
	  private long trackFrames;
	  
	  private float frameDuration;
	  
	  private float duration;
	  
	  private int framecounter;
	  
	  private StringBuilder vbrinfo;
	  
	  private byte[] vbrtoc;
	  
	  private int tocNumber;
	  
	  private int tocPer;
	  
	  private int tocFactor;
	  
	  private String strBitRate;
	  
	  private StringBuilder progress;
	  
	  private int progress_index;
	  
	  public void initialize(long paramLong, int paramInt) {
	    this.trackLength = paramLong;
	    this.duration = paramInt;
	    this.headerMask = -2097152;
	    this.progress_index = 1;
	    this.sync = false;
	    this.trackFrames = 0L;
	    this.sideinfosize = this.tocNumber = this.tocPer = this.tocFactor = this.framecounter = 0;
	    this.vbrtoc = null;
	    this.strBitRate = null;
	    this.progress = null;
	  }
	  
	  private void parseHeader(int paramInt) {
	    this.verID = paramInt >> 19 & 0x3;
	    this.layer = 4 - (paramInt >> 17) & 0x3;
	    this.protection_bit = paramInt >> 16 & 0x1;
	    this.bitrate_index = paramInt >> 12 & 0xF;
	    this.sampling_frequency = paramInt >> 10 & 0x3;
	    this.padding_bit = paramInt >> 9 & 0x1;
	    this.mode = paramInt >> 6 & 0x3;
	    this.mode_extension = paramInt >> 4 & 0x3;
	    this.isMS = (this.mode == 1 && (this.mode_extension & 0x2) != 0);
	    this.isIntensity = (this.mode == 1 && (this.mode_extension & 0x1) != 0);
	    this.lsf = (this.verID == 3) ? 0 : 1;
	    switch (this.layer) {
	      case 1:
	        this.framesize = this.bitrate[this.lsf][0][this.bitrate_index] * 12000;
	        this.framesize /= this.samplingRate[this.verID][this.sampling_frequency];
	        this.framesize = this.framesize + this.padding_bit << 2;
	        break;
	      case 2:
	        this.framesize = this.bitrate[this.lsf][1][this.bitrate_index] * 144000;
	        this.framesize /= this.samplingRate[this.verID][this.sampling_frequency];
	        this.framesize += this.padding_bit;
	        break;
	      case 3:
	        this.framesize = this.bitrate[this.lsf][2][this.bitrate_index] * 144000;
	        this.framesize /= this.samplingRate[this.verID][this.sampling_frequency] << this.lsf;
	        this.framesize += this.padding_bit;
	        if (this.verID == 3) {
	          this.sideinfosize = (this.mode == 3) ? 17 : 32;
	          break;
	        } 
	        this.sideinfosize = (this.mode == 3) ? 9 : 17;
	        break;
	    } 
	    this.maindatasize = this.framesize - 4 - this.sideinfosize;
	    if (this.protection_bit == 0)
	      this.maindatasize -= 2; 
	  }
	  
	  private int byte2int(byte[] paramArrayOfbyte, int paramInt) {
	    int i = paramArrayOfbyte[paramInt++] & 0xFF;
	    i <<= 8;
	    i |= paramArrayOfbyte[paramInt++] & 0xFF;
	    i <<= 8;
	    i |= paramArrayOfbyte[paramInt++] & 0xFF;
	    i <<= 8;
	    i |= paramArrayOfbyte[paramInt] & 0xFF;
	    return i;
	  }
	  
	  private int byte2short(byte[] paramArrayOfbyte, int paramInt) {
	    int i = paramArrayOfbyte[paramInt++] & 0xFF;
	    i <<= 8;
	    i |= paramArrayOfbyte[paramInt] & 0xFF;
	    return i;
	  }
	  
	  private boolean available(int paramInt1, int paramInt2) {
	    return ((paramInt1 & paramInt2) == paramInt2 && (paramInt1 >> 19 & 0x3) != 1 && (paramInt1 >> 17 & 0x3) != 0 && (paramInt1 >> 12 & 0xF) != 15 && (paramInt1 >> 12 & 0xF) != 0 && (paramInt1 >> 10 & 0x3) != 3);
	  }
	  
	  public int offset() {
	    return this.idx;
	  }
	  
	  public boolean syncFrame(byte[] paramArrayOfbyte, int paramInt1, int paramInt2) {
	    int j = 0;
	    int k = 0;
	    this.idx = paramInt1;
	    if (paramInt2 - this.idx <= 4)
	      return false; 
	    int i = byte2int(paramArrayOfbyte, this.idx);
	    this.idx += 4;
	    while (true) {
	      while (!available(i, this.headerMask)) {
	        i = i << 8 | paramArrayOfbyte[this.idx++] & 0xFF;
	        if (this.idx == paramInt2) {
	          this.idx -= 4;
	          return false;
	        } 
	      } 
	      if (this.idx > 4 + paramInt1) {
	        this.sync = false;
	        k += this.idx - paramInt1 - 4;
	      } 
	      parseHeader(i);
	      if (this.idx + this.framesize > paramInt2 + 4) {
	        this.idx -= 4;
	        return false;
	      } 
	      if (this.sync)
	        break; 
	      if (this.idx + this.framesize > paramInt2) {
	        this.idx -= 4;
	        return false;
	      } 
	      j = -2097152;
	      j |= i & 0x180000;
	      j |= i & 0x60000;
	      j |= i & 0xC00;
	      if (available(byte2int(paramArrayOfbyte, this.idx + this.framesize - 4), j)) {
	        if (this.headerMask == -2097152) {
	          this.headerMask = j;
	          this.trackFrames = this.trackLength / this.framesize;
	          parseVBR(paramArrayOfbyte, this.idx);
	          this.frameDuration = 1152.0F / (getSamplingRate() << this.lsf);
	          if (this.trackFrames == 0L)
	            this.trackFrames = (long)(this.duration / this.frameDuration); 
	          if (this.trackLength == 0L)
	            this.trackLength = this.trackFrames * this.framesize; 
	          this.duration = this.frameDuration * (float)this.trackFrames;
	        } 
	        this.sync = true;
	        break;
	      } 
	      i = i << 8 | paramArrayOfbyte[this.idx++] & 0xFF;
	    } 
	    if (this.protection_bit == 0)
	      this.idx += 2; 
	    this.framecounter++;
	    if (k > 0)
	      System.out.printf("frame# %d, skip bytes: %d\n", new Object[] { Integer.valueOf(this.framecounter), Integer.valueOf(k) }); 
	    return true;
	  }
	  
	  public boolean isMS() {
	    return this.isMS;
	  }
	  
	  public boolean isIntensityStereo() {
	    return this.isIntensity;
	  }
	  
	  public int getBitrate() {
	    return this.bitrate[this.lsf][this.layer - 1][this.bitrate_index];
	  }
	  
	  public int getBitrateIndex() {
	    return this.bitrate_index;
	  }
	  
	  public int getChannels() {
	    return (this.mode == 3) ? 1 : 2;
	  }
	  
	  public int getMode() {
	    return this.mode;
	  }
	  
	  public int getModeExtension() {
	    return this.mode_extension;
	  }
	  
	  public int getVersion() {
	    return this.verID;
	  }
	  
	  public int getLayer() {
	    return this.layer;
	  }
	  
	  public int getSamplingFrequency() {
	    return this.sampling_frequency;
	  }
	  
	  public int getSamplingRate() {
	    return this.samplingRate[this.verID][this.sampling_frequency];
	  }
	  
	  public int getMainDataSize() {
	    return this.maindatasize;
	  }
	  
	  public int getSideInfoSize() {
	    return this.sideinfosize;
	  }
	  
	  public int getFrameSize() {
	    return this.framesize;
	  }
	  
	  public int getPcmSize() {
	    int i = (this.verID == 3) ? 4608 : 2304;
	    if (this.mode == 3)
	      i >>= 1; 
	    return i;
	  }
	  
	  public long getTackLength() {
	    return this.trackLength;
	  }
	  
	  public int getFrames() {
	    return this.framecounter;
	  }
	  
	  public long getTrackFrames() {
	    return this.trackFrames;
	  }
	  
	  public float getFrameDuration() {
	    return this.frameDuration;
	  }
	  
	  public float getDuration() {
	    return this.duration;
	  }
	  
	  public int getElapse() {
	    return (int)(this.framecounter * this.frameDuration);
	  }
	  
	  public String getVBRInfo() {
	    return (this.vbrinfo == null) ? null : this.vbrinfo.toString();
	  }
	  
	  private void parseVBR(byte[] paramArrayOfbyte, int paramInt) {
	    this.vbrinfo = null;
	    int i = paramInt + this.framesize - 4;
	    if (i >= paramArrayOfbyte.length)
	      return; 
	    int j;
	    for (j = 2; j < this.sideinfosize; j++) {
	      if (paramArrayOfbyte[paramInt + j] != 0)
	        return; 
	    } 
	    paramInt += this.sideinfosize;
	    if ((paramArrayOfbyte[paramInt] == 88 && paramArrayOfbyte[paramInt + 1] == 105 && paramArrayOfbyte[paramInt + 2] == 110 && paramArrayOfbyte[paramInt + 3] == 103) || (paramArrayOfbyte[paramInt] == 73 && paramArrayOfbyte[paramInt + 1] == 110 && paramArrayOfbyte[paramInt + 2] == 102 && paramArrayOfbyte[paramInt + 3] == 111)) {
	      if (i - paramInt < 120)
	        return; 
	      paramInt = xinginfoHeader(paramArrayOfbyte, paramInt);
	    } else if (paramArrayOfbyte[paramInt] == 86 && paramArrayOfbyte[paramInt + 1] == 66 && paramArrayOfbyte[paramInt + 2] == 82 && paramArrayOfbyte[paramInt + 3] == 73) {
	      if (i - paramInt < 26)
	        return; 
	      paramInt = vbriHeader(paramArrayOfbyte, paramInt);
	      j = this.tocNumber * this.tocPer;
	      if (i - paramInt < j)
	        return; 
	      this.vbrinfo.append("\n          TOC: ");
	      this.vbrinfo.append(this.tocNumber);
	      this.vbrinfo.append(" * ");
	      this.vbrinfo.append(this.tocPer);
	      this.vbrinfo.append(", factor = ");
	      this.vbrinfo.append(this.tocFactor);
	      this.vbrtoc = new byte[j];
	      System.arraycopy(paramArrayOfbyte, paramInt, this.vbrtoc, 0, j);
	      paramInt += j;
	    } else {
	      return;
	    } 
	    if (i - paramInt < 36 || paramArrayOfbyte[paramInt] == 0) {
	      this.strBitRate = "VBR";
	      return;
	    } 
	    String str = new String(paramArrayOfbyte, paramInt, 9);
	    paramInt += 9;
	    this.vbrinfo.append("\n      encoder: ");
	    this.vbrinfo.append(str);
	    int k = (paramArrayOfbyte[paramInt] & 0xFF) >> 4;
	    int m = paramArrayOfbyte[paramInt++] & 0xF;
	    int n = paramArrayOfbyte[paramInt++] & 0xFF;
	    this.vbrinfo.append("\n      lowpass: ");
	    this.vbrinfo.append(n * 100);
	    this.vbrinfo.append("Hz");
	    this.vbrinfo.append("\n     revision: ");
	    this.vbrinfo.append(k);
	    float f = Float.intBitsToFloat(byte2int(paramArrayOfbyte, paramInt));
	    paramInt += 4;
	    int i1 = byte2short(paramArrayOfbyte, paramInt);
	    paramInt += 2;
	    int i2 = byte2short(paramArrayOfbyte, paramInt);
	    paramInt += 2;
	    paramInt++;
	    int i3 = paramArrayOfbyte[paramInt++] & 0xFF;
	    switch (m) {
	      case 1:
	      case 8:
	        this.strBitRate = String.format("CBR %1$dK", new Object[] { Integer.valueOf(getBitrate()) });
	        break;
	      case 2:
	      case 9:
	        if (i3 < 255) {
	          this.strBitRate = String.format("ABR %1$dK", new Object[] { Integer.valueOf(i3) });
	          break;
	        } 
	        this.strBitRate = String.format("ABR %1$dK以上", new Object[] { Integer.valueOf(i3) });
	        break;
	      default:
	        if (i3 == 0) {
	          this.strBitRate = "VBR";
	          break;
	        } 
	        this.strBitRate = String.format("VBR(min%dK)", new Object[] { Integer.valueOf(i3) });
	        break;
	    } 
	    paramInt += 3;
	    paramInt++;
	    byte b = paramArrayOfbyte[paramInt++];
	    if (b != 0)
	      System.out.println("    MP3 Gain: " + b + " [psa=" + f + ",rrg=" + i1 + ",arg=" + i2 + "]"); 
	    int i4 = byte2short(paramArrayOfbyte, paramInt);
	    int i5 = i4 >> 11 & 0x7;
	    switch (i5) {
	      case 1:
	        this.vbrinfo.append("\n     surround: DPL");
	        break;
	      case 2:
	        this.vbrinfo.append("\n     surround: DPL2");
	        break;
	      case 3:
	        this.vbrinfo.append("\n     surround: Ambisonic");
	        break;
	      case 7:
	        this.vbrinfo.append("\n     surround: invalid data");
	        break;
	    } 
	    i4 &= 0x7FF;
	    if (i4 != 0) {
	      this.vbrinfo.append("\n     surround: preset ");
	      this.vbrinfo.append(i4);
	    } 
	    paramInt += 2;
	    int i6 = byte2int(paramArrayOfbyte, paramInt);
	    paramInt += 4;
	    if (i6 != 0)
	      this.trackLength = i6; 
	    paramInt += 2;
	  }
	  
	  private int vbriHeader(byte[] paramArrayOfbyte, int paramInt) {
	    if (this.vbrinfo == null)
	      this.vbrinfo = new StringBuilder(); 
	    this.vbrinfo.append("   vbr header: vbri");
	    int i = byte2short(paramArrayOfbyte, paramInt + 8);
	    this.vbrinfo.append("\n      quality: ");
	    this.vbrinfo.append(i);
	    this.trackLength = byte2int(paramArrayOfbyte, paramInt + 10);
	    this.vbrinfo.append("\n  track bytes: ");
	    this.vbrinfo.append(this.trackLength);
	    this.trackFrames = byte2int(paramArrayOfbyte, paramInt + 14);
	    this.vbrinfo.append("\n track frames: ");
	    this.vbrinfo.append(this.trackFrames);
	    this.tocNumber = byte2short(paramArrayOfbyte, paramInt + 18);
	    this.tocFactor = byte2short(paramArrayOfbyte, paramInt + 20);
	    this.tocPer = byte2short(paramArrayOfbyte, paramInt + 22);
	    int j = byte2short(paramArrayOfbyte, paramInt + 24);
	    this.vbrinfo.append("\n   toc frames: ");
	    this.vbrinfo.append(j);
	    paramInt += 26;
	    return paramInt;
	  }
	  
	  private int xinginfoHeader(byte[] paramArrayOfbyte, int paramInt) {
	    if (this.vbrinfo == null)
	      this.vbrinfo = new StringBuilder(); 
	    this.vbrinfo.append("   vbr header: ");
	    this.vbrinfo.append(new String(paramArrayOfbyte, paramInt, 4));
	    this.trackLength -= this.framesize;
	    int i = byte2int(paramArrayOfbyte, paramInt + 4);
	    if ((i & 0x1) == 1) {
	      this.trackFrames = byte2int(paramArrayOfbyte, paramInt + 8);
	      this.vbrinfo.append("\n track frames: ");
	      this.vbrinfo.append(this.trackFrames);
	      paramInt += 4;
	    } 
	    paramInt += 8;
	    if ((i & 0x2) != 0) {
	      this.trackLength = byte2int(paramArrayOfbyte, paramInt);
	      paramInt += 4;
	      this.vbrinfo.append("\n  track bytes: ");
	      this.vbrinfo.append(this.trackLength);
	    } 
	    if ((i & 0x4) != 0) {
	      this.vbrtoc = new byte[100];
	      System.arraycopy(paramArrayOfbyte, paramInt, this.vbrtoc, 0, 100);
	      paramInt += 100;
	    } 
	    if ((i & 0x8) != 0) {
	      int j = byte2int(paramArrayOfbyte, paramInt);
	      paramInt += 4;
	      this.vbrinfo.append("\n      quality: ");
	      this.vbrinfo.append(j);
	    } 
	    this.tocNumber = 100;
	    this.tocPer = 1;
	    this.tocFactor = 1;
	    return paramInt;
	  }
	  
	  public void printHeaderInfo() {
	    if (this.headerMask == -2097152)
	      return; 
	    float f = (float)this.trackFrames * this.frameDuration;
	    int i = (int)(f / 60.0F);
	    String str = String.format("%02d:%02d", new Object[] { Integer.valueOf(i), Integer.valueOf((int)((f - (i * 60)) + 0.5D)) });
	    if (this.strBitRate == null)
	      this.strBitRate = String.format("%dK", new Object[] { Integer.valueOf(this.bitrate[this.lsf][this.layer - 1][this.bitrate_index]) }); 
	    StringBuilder stringBuilder = new StringBuilder();
	    if (this.verID == 0) {
	      stringBuilder.append("MPEG-2.5");
	    } else if (this.verID == 2) {
	      stringBuilder.append("MPEG-2");
	    } else if (this.verID == 3) {
	      stringBuilder.append("MPEG-1");
	    } 
	    stringBuilder.append(", Layer ");
	    stringBuilder.append(this.layer);
	    stringBuilder.append(", ");
	    stringBuilder.append(getSamplingRate());
	    stringBuilder.append("Hz, ");
	    stringBuilder.append(this.strBitRate);
	    if (this.mode == 0) {
	      stringBuilder.append(", Stereo");
	    } else if (this.mode == 1) {
	      stringBuilder.append(", Joint Stereo");
	    } else if (this.mode == 2) {
	      stringBuilder.append(", Dual channel");
	    } else if (this.mode == 3) {
	      stringBuilder.append(", Single channel(Mono)");
	    } 
	    if (this.mode_extension == 0) {
	      stringBuilder.append(", ");
	    } else if (this.mode_extension == 1) {
	      stringBuilder.append("(I/S), ");
	    } else if (this.mode_extension == 2) {
	      stringBuilder.append("(M/S), ");
	    } else if (this.mode_extension == 3) {
	      stringBuilder.append("(I/S & M/S), ");
	    } 
	    stringBuilder.append(str);
	    System.out.println(stringBuilder.toString());
	  }
	  
	  public void printVBRTag() {
	    if (this.vbrinfo != null)
	      System.out.println(this.vbrinfo.toString()); 
	  }
	  
	  public void printProgress() {
	    float f1 = this.framecounter * this.frameDuration;
	    int i = (int)(f1 / 60.0F);
	    float f2 = f1 - (60 * i);
	    int j = ((int)((100.0F * this.framecounter / (float)this.trackFrames) + 0.5D) << 2) / 10;
	    if (this.progress == null)
	      this.progress = new StringBuilder(">----------------------------------------"); 
	    if (j == this.progress_index) {
	      this.progress.replace(j - 1, j + 1, "=>");
	      this.progress_index++;
	    } 
	    System.out.printf("\r#%-5d [%-41s] %02d:%05.2f ", new Object[] { Integer.valueOf(this.framecounter), this.progress, Integer.valueOf(i), Float.valueOf(f2) });
	  }
	}

